package com.feinno.urcs.group.siptask;

import com.feinno.sip.header.CSeq;
import com.feinno.sip.header.CallID;
import com.feinno.sip.header.Expires;
import com.feinno.sip.header.RouteList;
import com.feinno.sip.message.SipRequest;
import com.feinno.sip.message.SipResponse;
import com.feinno.sip.parser.RouteParser;
import com.feinno.sip.transaction.TransactionClient;
import com.feinno.sip.transaction.TransactionClientListener;
import com.feinno.urcs.group.GroupConfiguration;
import com.feinno.urcs.group.GroupConstants;
import com.feinno.urcs.group.api.GroupSubscriptionManager;
import com.feinno.urcs.group.entity.*;
import com.feinno.urcs.group.entity.GroupChatMemberNotifyInfo.MemberInfo;
import com.feinno.urcs.group.siptask.task.GroupInfoNotifyArgs;
import com.feinno.urcs.group.siptask.task.GroupInfoNotifyType;
import com.feinno.urcs.group.siptask.task.GroupTaskEvents;
import com.feinno.urcs.sip.SipConnector;
import com.feinno.urcs.sip.UrcsSipFactory;
import com.feinno.urcs.sipservlet.SipConnectorLoader;
import com.feinno.urcs.sipservlet.SipServletFuture;
import com.feinno.urcs.sipservlet.SipServletRequest;
import com.feinno.urcs.sipservlet.SipServletRequestFactory;
import com.feinno.urcs.sipservlet.message.SipMessageBuilder;
import com.feinno.urcs.types.MobileNo;
import com.feinno.util.StringUtils;
import org.helium.framework.annotations.FieldSetter;
import org.helium.framework.annotations.ServiceSetter;
import org.helium.framework.annotations.TaskImplementation;
import org.helium.framework.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.address.TelURI;
import javax.sip.message.Request;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Li.Hongbo <lihongbo@feinno.com>
 */
@TaskImplementation(event = GroupTaskEvents.GROUP_INFO_NOTIFY)
public class GroupInfoNotifyTask implements Task<GroupInfoNotifyArgs> {
	private static final Logger LOGGER = LoggerFactory.getLogger(GroupInfoNotifyTask.class);
	@FieldSetter("${CSCF_ADDRESS}")
	private String cscfAddress;
	@FieldSetter(loader = SipConnectorLoader.class, value = "${SIP_STACK_AS}")
	private SipConnector connector;
	@ServiceSetter
	private GroupSubscriptionManager subscriptionManager;


	@Override
	public void processTask(GroupInfoNotifyArgs args) {
		LOGGER.info("GroupInfoNotifyTask {} {}", args.getGroupInfo().getGroupId(), args.getToMemberMobileNo());
		try {
			SipServletRequest request = createGroupInfoNotify(args);
			SipServletFuture future = request.sendRequest();
			future.setListener(new TransactionClientListener() {
				@Override
				public void onTransProvisionalResponse(TransactionClient transactionClient, SipResponse sipResponse) {
					// 100 180
				}

				@Override
				public void onTransSuccessResponse(TransactionClient transactionClient, SipResponse sipResponse) {
					// 200

				}

				@Override
				public void onTransFailureResponse(TransactionClient transactionClient, SipResponse sipResponse) {
					// 4xx
					// 5xx
					if (sipResponse.getStatusCode() == 481) {
						MobileNo toMemberMobileNo = args.getToMemberMobileNo();
						subscriptionManager.unsubscribeConference(args.getGroupInfo().getGroupId(), toMemberMobileNo);
					}
				}

				@Override
				public void onTransTimeout(TransactionClient transactionClient) {
					// TIMEOUT

				}
			});
		} catch (Exception ex) {
			LOGGER.error("GroupInfoNotifyTask.processTask error : {}", ex);
		}
	}

	public SipServletRequest createGroupInfoNotify(GroupInfoNotifyArgs args) throws Exception {
		int groupId = args.getGroupInfo().getGroupId();
		int expires = subscriptionManager.getExpires(groupId, args.getToMemberMobileNo());
		SipURI groupUri = UrcsSipFactory.getAddressFactory().createSipURI(GroupConfiguration.INSTANCE.getGroupPrefix() + groupId, GroupConfiguration.INSTANCE.getGroupDomain());
		TelURI toTel = UrcsSipFactory.getAddressFactory().createTelURI(args.getToMemberMobileNo().toString());
		Address fromAddress = UrcsSipFactory.getAddressFactory().createAddress(groupUri);
		Address toAddress = UrcsSipFactory.getAddressFactory().createAddress(toTel);
		SipRequest sipRequest = UrcsSipFactory.getMessageFactory().createRequest(connector.getSipProvider(), Request.NOTIFY, toAddress, fromAddress);
		sipRequest.setFromTag(args.getFromTag());
		sipRequest.setToTag(args.getToTag());
		SipServletRequest sipServletRequest = SipServletRequestFactory.createRequest(sipRequest, connector);
		SipMessageBuilder builder = new SipMessageBuilder(sipServletRequest);
		Expires expiresHeader = new Expires();
		expiresHeader.setExpires(expires);
		builder.from(groupUri)
				.to(toTel)
				.fromTag(args.getFromTag())
				.toTag(args.getToTag())
				.requestUri(UrcsSipFactory.getAddressFactory().createAddress(args.getContact()).getURI())
				.removeHeader(CallID.NAME)
				.callId(args.getCallId())
				.removeHeader(CSeq.NAME)
				.cSeq(args.getCseq(), Request.NOTIFY)
				.pai(groupUri)
				.allow("INVITE,UPDATE,ACK,CANCEL,BYE,NOTIFY,OPTIONS,MESSAGE,REFER")
				.contact(groupUri.getUser(), groupUri.getHost(), GroupConstants.G_3GPP_ICSI_REF_SESSION_KEY_VALUE, true)
				.maxForwards(70)
				.supported("timer,recipient-list-invite")
				.subscriptionState(expires)
				.event("conference")
				.allowEvents("conference,grouplist")
				.accept("application", "sdp")
				.userAgent("Conf-serv/3GPP")
				.setHeader(expiresHeader);
		byte[] contentXML = createContentXML(args, args.getGroupInfo(), 0);
		if (contentXML.length >= 13 * 1024) {
			builder.gzipContent(contentXML, "application", "conference-info+xml");
		} else {
			builder.content(contentXML, "application", "conference-info+xml");
		}
		if (args.getRoute().size() > 0) {
			for (String r : args.getRoute()) {
				RouteList routes = (RouteList) new RouteParser(r).parse();
				if (routes.size() > 0) {
					routes.forEach(builder::addHeader);
				}
			}
		}
		SipServletRequest notify = builder.toSipServletReqeust();
		return notify;
	}

	public byte[] createContentXML(GroupInfoNotifyArgs args, GroupInfo info, long version) throws ParseException {
		List<GroupMember> members = info.getMembers();
		/** conference-description */
		GroupChatMemberNotifyInfo.ConferenceDescription conferenceDescription = new GroupChatMemberNotifyInfo.ConferenceDescription();

		if (!StringUtils.isNullOrEmpty(info.getSubject()))
			conferenceDescription.setSubject(info.getSubject());

		conferenceDescription.setSubject(info.getSubject());
		conferenceDescription.setMaximum_user_count(String.valueOf(info.getLimitMemberCount()));
		/** conference-state */
		GroupChatMemberNotifyInfo.ConferenceState conferenceState = new GroupChatMemberNotifyInfo.ConferenceState();
		conferenceState.setLocked(false);
		GroupChatMemberNotifyInfo notifyInfo = new GroupChatMemberNotifyInfo();
		if (args.isDismiss()) {
			//如果是解散群, 发一个空内容,并未deleted 的东西
			notifyInfo.setState("deleted");
			GroupIndex index = new GroupIndex(info.getGroupId(), info.getLogicalPool());
			notifyInfo.setEntity(index.getConferenceUri());
			return notifyInfo.toXmlByteArray();
		}
		conferenceState.setActive(true);
		/** user-policy */
		GroupChatMemberNotifyInfo.UserPolicy userPolicy = new GroupChatMemberNotifyInfo.UserPolicy();
		userPolicy.setOfflinesm(1); // TODO
		/** users */
		List<GroupChatMemberNotifyInfo.MemberInfo> memberInfos = new ArrayList<MemberInfo>();
		int count = 0;
		for (GroupMember member : members) {
			if (member.getIdentity() != 1 && member.getIdentity() != 3) {
				continue;
			}
			count++;
			GroupChatMemberNotifyInfo.MemberInfo memberInfo = new GroupChatMemberNotifyInfo.MemberInfo();
			TelURI telURL = UrcsSipFactory.getAddressFactory().createTelURI(member.getMobileNo().toString());
			memberInfo.setEntity(telURL.toString());
			GroupChatMemberNotifyInfo.Roles roles = new GroupChatMemberNotifyInfo.Roles();
			if (member.getRole() == GroupMemberRole.CHAIRMAN) {
				roles.setEntry("chairman");
			} else {
				roles.setEntry("participant");
			}
			memberInfo.setRoles(roles);
			GroupChatMemberNotifyInfo.UserEndpoint endpoint = new GroupChatMemberNotifyInfo.UserEndpoint();
			endpoint.setEntity(telURL.toString());

			if (!StringUtils.isNullOrEmpty(member.getDisplayname()))
				endpoint.setDisplayname(member.getDisplayname());
			endpoint.setStatus(member.getStatus().toString().toLowerCase());
			if (GroupInfoNotifyType.DELETED.equals(args.getNotifyType())) {
				if (member.getMobileNo().equals(args.getQuitMember())) {
					memberInfo.setState("deleted");

					endpoint.setStatus(GroupMemberStatus.DISCONNECTED.name().toLowerCase());
					endpoint.setDisConMethod("departed");
				}
			}
			GroupChatMemberNotifyInfo.SupportedMedias supportedMedias = new GroupChatMemberNotifyInfo.SupportedMedias();
			supportedMedias.setXmlns("dconf:conference-info");
			endpoint.setSupportedMedias(supportedMedias);
			memberInfo.setEndpoint(endpoint);
			memberInfo.setEtype("gpmanage");
			memberInfos.add(memberInfo);
		}
		conferenceState.setUserCount(String.valueOf(count));
		notifyInfo.setUsers(memberInfos);
		notifyInfo.setConferenceDescription(conferenceDescription);
		notifyInfo.setConferenceState(conferenceState);
		notifyInfo.setVersion(String.valueOf(version));
		GroupIndex index = new GroupIndex(info.getGroupId(), info.getLogicalPool());
		notifyInfo.setEntity(index.getConferenceUri());
		return notifyInfo.toXmlByteArray();
	}
}
